#include "updatercore.h"
#include "server.h"
#include "extract.h"

//#include <QDebug>

////////////////////////////////////////////////////////////////////////////////

UpdaterCore::UpdaterCore()
{
  m_handler = 0;
  m_server = new Server();
}

UpdaterCore::~UpdaterCore()
{
  delete m_server;
}

void UpdaterCore::runDelayed()
{
  // execute program-after
  if (!m_input.run_after.isEmpty())
  {
  //  QMessageBox::about(0, "", m_input.run_after);
    if (m_input.run_after_dir.isEmpty())
      QProcess::startDetached(m_input.run_after);
    else {
      //QDir::setCurrent(m_input.run_after_dir);
      QProcess::startDetached(m_input.run_after, QStringList(), m_input.run_after_dir);
    }
  }
}

void UpdaterCore::setInputData(const InputData &data)
{
  m_input = data;

  ServerInfo info;
  info.host = m_input.server;
  info.port = 80;
  info.dir = "/";
  m_server->setInfo(info);
}

int UpdaterCore::getServerVersion()
{
  // connect
  bool conn = m_server->connectServer();
  if (!conn)
    return -1;

  // retrieve version
  int res = m_server->getFile("", m_input.local_dir, m_input.version_file);
  if (!res)
    return -2;

  // parse version file
  readVersionFile(m_input.local_dir + "/" + m_input.version_file);

  return res;
}

QString getValue(QString &input)
{
  QTextStream ts(&input);
  QString tmp; ts >> tmp;
  return input.mid(tmp.length()+1, input.length());
}

bool UpdaterCore::readVersionFile(const QString &filename)
{
  m_info = VersionInfo();

  QFile infile(filename);
  if (!infile.open(QFile::ReadOnly))
    return false;

  bool isCurrent = false;
  bool isArch = false;
  bool isModule = false;
  QSet<QString> modules;
  QString path;

  QTextStream ts(&infile);
  while (!ts.atEnd())
  {
    QString line = ts.readLine().simplified();
    if (line.isEmpty() || line.startsWith('#'))
      continue;

    if (line.startsWith("$version"))
    {
      int ver = getValue(line).toInt();

//      qDebug() << line;
//      qDebug() << ver;

      if (ver > m_info.latest_version)
      {
        m_info.latest_version = ver;
        isCurrent = true;
        continue;
      }

      isCurrent = false;

      if (ver <= m_input.current_version)
        return true;

      continue;
    }

    if (isCurrent && line.startsWith("$txtversion"))
    {
      m_info.txt_version = getValue(line);
      continue;
    }

    if (line.startsWith("$arch"))
    {
      QString arch = getValue(line);
      isArch = (arch == "common" || arch == m_input.architecture);
      continue;
    }

    if (!isArch)
      continue;

    if (line.startsWith("$path"))
    {
      path = getValue(line);
      continue;
    }

    if (line.startsWith("$module"))
    {
      ModuleInfo mi;
      mi.name = getValue(line);

      // check if newer version already in the list
      if (modules.contains(mi.name))
      {
        isModule = false;
        continue;
      }

      isModule = true;
      modules.insert(mi.name);

      mi.server_path = path;
      m_info.modules.append(mi);
      continue;
    }

    if (!isModule)
      continue;

    if (line.startsWith("$file"))
    {
      m_info.modules.last().file_name = getValue(line);
      continue;
    }

    if (line.startsWith("$target"))
    {
      m_info.modules.last().install_path = getValue(line);
      continue;
    }
  }

  return true;
}

int UpdaterCore::updateAll()
{
  int res = downloadModules();
  if (res <= 0)
    return -1;

  res = installModules();
  if (res <= 0)
    return -2;

  return res;
}

int UpdaterCore::downloadModules()
{
  int total = m_info.modules.count();

  // download all
  int cnt = 0;
  for (int i = 0; i < total; i++)
  {
    const ModuleInfo &mi = m_info.modules.at(i);
    if (m_handler)
      m_handler->onModuleDownload(mi);

    int res = m_server->getFile(mi.server_path, m_input.local_dir, mi.file_name);
    if (res > 0)
      cnt++;
  }

  if (m_handler)
    m_handler->onDownloadFinished(total, cnt);

  return cnt == total ? cnt : -1;
}

int UpdaterCore::writeInstallList(const QString &filename)
{
  QFile list(filename);
  if (!list.open(QFile::WriteOnly))
    return -1;
  QTextStream ts(&list);

  int total = m_info.modules.count();
  for (int i = 0; i < total; i++)
  {
    const ModuleInfo &mi = m_info.modules.at(i);

    QString install_path = mi.install_path;

    // make target path
    QMap<QString,QString>::iterator it, it_end = m_input.path_subst.end();
    for (it = m_input.path_subst.begin(); it != it_end; it++)
    {
      install_path = install_path.replace(it.key(), it.value());
    }

    // write list entry
    ts << m_input.local_dir + "/" + mi.file_name;
    ts << "\n";
    ts << install_path;
    ts << "\n\n";
  }

  return total;
}
int UpdaterCore::installModules()
{
  int total = m_info.modules.count();

  // install
  int cnt = 0;
  for (int i = 0; i < total; i++)
  {
    const ModuleInfo &mi = m_info.modules.at(i);
    if (m_handler)
      m_handler->onModuleInstall(mi);

    QString install_path;
    int res = installModule(mi, install_path);

    // clean
    QFileInfo fi(m_input.local_dir + "/" + mi.file_name);
    QFile::remove(fi.canonicalFilePath());

    if (res > 0)
      cnt++;
  }

  if (m_handler)
    m_handler->onInstallFinished(total, cnt);

  // finish up

  if (m_handler)
    m_handler->onFinished();

  return cnt == total ? cnt : -1;
}

int UpdaterCore::installModules(const QString &filename)
{
  QFile list(filename);
  if (!list.open(QFile::ReadOnly))
    return -1;

  QStringList module, target;
  QTextStream ts(&list);
  bool mod = true;
  while (!ts.atEnd())
  {
    QString line = ts.readLine().simplified();
    if (line.isEmpty())
      continue;

    if (mod)
      module.append(line);
    else
      target.append(line);

    mod = !mod;
  }

  for (int i = 0; i < target.count(); i++)
  {
    if (installModule(module.at(i), target.at(i)) <= 0)
      return -2;
  }

  return target.count();
}

int UpdaterCore::installModule(const QString &path, const QString &install_path)
{
  // create dir if not exists
  QDir dir(install_path);
  if (!dir.exists())
  {
    dir.mkpath(install_path);
  }

  // extract package
  QFile pack(path);
  if (!pack.exists())
    return -1;

  QFileInfo packinfo(pack);

  int res = unzipAll(packinfo.absoluteFilePath().toAscii().data(),
                     install_path.toAscii().data());
  if (res < 0)
      return -2;

  return 1;
}

int UpdaterCore::installModule(const ModuleInfo &mi, QString &install_path)
{
  /*QString*/ install_path = mi.install_path;

  // make target path
  QMap<QString,QString>::iterator it, it_end = m_input.path_subst.end();
  for (it = m_input.path_subst.begin(); it != it_end; it++)
  {
    install_path = install_path.replace(it.key(), it.value());
  }

  return installModule(m_input.local_dir + "/" + mi.file_name, install_path);
}
